package com.springboot.demo.io;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 文件io测试
 *
 * @author supu
 * @since 2020-11-16 16:50
 **/
public class FileIoTest {
    public static void main(String[] args) {
        int count = 1000000;
        String content = "123456789\n";

        generalWrite(content, count);

        bufferWrite(content, count);

        mmapWrite(content, count);
    }

    /**
     * 频繁写文件，性能最差
     * 
     * @param content 内容
     * @param count 写文件次数
     */
    private static void generalWrite(String content, int count) {
        long start = System.currentTimeMillis();
        File file = new File("D:/test1.txt");

        try {
            FileOutputStream outputStream = new FileOutputStream(file);

            for (int i = 0; i < count; i++) {
                outputStream.write(content.getBytes());
            }

            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("generalWrite 耗时 : " + (System.currentTimeMillis() - start));
    }

    /**
     * 频繁写文件，性能次之
     *
     * @param content 内容
     * @param count 写文件次数
     */
    private static void bufferWrite(String content, int count) {
        long start = System.currentTimeMillis();
        File file = new File("D:/test2.txt");

        try {
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
            // jvm syscall write(byte[])
            for (int i = 0; i < count; i++) {
                outputStream.write(content.getBytes());
            }

            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("bufferWrite 耗时 : " + (System.currentTimeMillis() - start));
    }

    /**
     * 频繁写文件，性能最好
     *
     * @param content 内容
     * @param count 写文件次数
     */
    private static void mmapWrite(String content, int count) {
        long start = System.currentTimeMillis();

        try {
            RandomAccessFile accessFile = new RandomAccessFile("D:/test3.txt", "rw");

            // seek 任意位置随机写
           /* accessFile.write(content.getBytes());
            accessFile.seek(3);
            accessFile.write("hello".getBytes());*/

            FileChannel channel = accessFile.getChannel();
            // mmap 堆外 和文件映射的 byte not object
            MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 9800 * 1024);
            // 不是系统调用 但是数据会到达 内核的pagecache
            // 曾经我们是需要out.write() 这样的系统调用，才能让程序的data 进入内核的pagecache
            // 曾经必须有用户态内核态切换
            // mmap的内存映射，依然是内核的pagecache体系所约束的！！！
            // 换言之，丢数据
            // 你可以去github上找一些 其他C程序员写的jni扩展库，使用linux内核的Direct IO
            // 直接IO是忽略linux的pagecache
            // 是把pagecache 交给了程序自己开辟一个字节数组当作pagecache，动用代码逻辑来维护一致性/dirty。。。一系列复杂问题
            for (int i = 0; i < count; i++) {
                mappedByteBuffer.put(content.getBytes());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("mmapWrite 耗时 : " + (System.currentTimeMillis() - start));
    }
}
